package com.example.demo07cache.service;

import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.cache.annotation.*;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeService {

//    private final EmployeeMapper employeeMapper;
//
//    public EmployeeService(EmployeeMapper employeeMapper) {
//        this.employeeMapper = employeeMapper;
//    }
//
//    /**
//     * 将方法返回结果进行缓存。
//     * CacheManager 管理多个 Cache 组件，对缓存的真正的 CRUD 操作在 Cache 组件中，每一个缓存组件有自己唯一一个名字。
//     * <p>
//     * <p>
//     * 原理:
//     * 1. 自动配置类 {@link CacheAutoConfiguration}
//     * 2. 缓存的配置类
//     * org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
//     * org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
//     * 3. 哪个配置类默认生效 ?  {@link org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration}
//     * 4. 给容器中注册了一个 ConcurrentMapCacheManager (获取或者创建 ConcurrentMapCache 类型的缓存组件)
//     * <p>
//     * <p>
//     * 运行流程:
//     * 1. 在 @Cacheable 注解的方法运行之前，先去查询 Cache (缓存组件)，按照 cacheNames 指定的名字获取。
//     * (CacheManager 获取相应的 Cache，第一次获取 Cache 组件没有获取到就创建一个 Cache)
//     * 2. 使用一个 key 去 Cache 中查找缓存，默认的 key 就是方法的参数值
//     * (key 是按照某种策略生成的，默认是使用 SimpleKeyGenerator 生成)
//     * SimpleKeyGenerator 的生成策略:
//     * (1)如果没有参数: key = new SimpleKey()
//     * (2)如果参数只有一个: key = 参数的值
//     * (3)如果参数有多个: key = new SimpleKey(params)
//     * 3. 缓存中没有就调用目标方法
//     * 4. 将目标方法返回的结果放入缓存
//     *
//     * @Cacheable 标注的方法执行之前先去缓存中查询有没有这个数据，默认按照参数的值作为 key 去查询缓存，
//     * 如果缓存中没有就运行方法，将方法返回值放入缓存；以后再来调用就可以直接使用缓存中的数据。
//     * <p>
//     * <p>
//     * 核心:
//     * 1. 使用 CacheManager [ConcurrentMapCacheManager] 按照名字得到 Cache [ConcurrentMapCache] 组件
//     * 2. key 使用 KeyGenerator 生成的，默认是 SimpleKeyGenerator
//     * @Cacheable 的几个属性:
//     * 1、cacheNames | value (指定缓存组件的名字，将方法返回的结果放在哪个缓存，是数组的方式，可以指定多个缓存)
//     * <p>
//     * 2、key (缓存数据使用的 key，默认使用方法的参数值，可以写 SpEL 指定)
//     * 使用 SpELl 指定 key: key = "#root.methodName+'['+#id+']'"
//     * <p>
//     * 3、keyGenerator (key 的生成器，可以自己指定 key 的生成器的组件 id，key | keyGenerator 二选一使用)
//     * keyGenerator = "myKeyGenerator"
//     * @see KeyGenerator
//     * <p>
//     * 4、cacheManager (缓存管理器、可以是 Redis、可以是 ConcurrentHashMap)
//     * <p>
//     * 5、cacheResolver (缓存解析器、cacheManager | cacheResolver 二选一使用)
//     * <p>
//     * 6、condition (指定符合条件的情况下才缓存)
//     * condition = "#id>0"
//     * condition = "#a0>1"  第一个参数的值 > 1 才进行缓存
//     * <p>
//     * 7、unless (否定缓存、当 unless 指定的条件为 true，方法返回值就不会被缓存，可以获取到结果进行缓存)
//     * unless = "#result == null"
//     * unless = "#a0==2"   第一个参数的值 ==2 结果不缓存
//     * <p>
//     * 8、sync (是否使用异步模式)
//     */
//    @Cacheable(cacheNames = {"emp"}/*, keyGenerator = "myKeyGenerator", condition = "#a0>1", unless = "#a0==2"*/)
//    public Employee getEmpById(Integer id) {
//        System.out.println("查询 " + id + " 号员工");
//        return employeeMapper.getEmpById(id);
//    }
//
//
//    /**
//     * @CachePut 即调用方法，又更新缓存数据；修改了数据库的某条数据，同时更新缓存 (适用于插入和修改)。
//     * <p>
//     * 运行时机:
//     * 1. 先调用目标方法
//     * 2. 将目标方法的返回值缓存起来
//     * <p>
//     * 测试步骤:
//     * 1. 查询 1 号员工，查询到的数据放入了缓存；
//     * 2. 以后查询 1 号员工，直接从缓存中获取；
//     * 3. 更新 1 号员工: http://localhost:8080/emp?id=1&lastName=张强
//     * (1)将方法的返回值也放入了缓存;
//     * (2)key: 传入的 employee 对象; value: 返回的 employee 对象
//     * (3)key 的不同导致没有跟新缓存
//     * 4. 查询 1 号员工？应该是更新后的员工信息。
//     * <p>
//     * 为什么没更新？？
//     * <p>
//     * 怎么解决？？
//     * 将 key 指定成同一个。
//     * (1) key = "#employee.id"
//     * (2) key = "#result.id"
//     */
//    @CachePut(cacheNames = {"emp"}, key = "#result.id")
//    public Employee updateEmp(Employee employee) {
//        System.out.println("updateEmp: " + employee);
//        employeeMapper.updateEmp(employee);
//        return employee;
//    }
//
//    /**
//     * @CacheEvict 缓存清除
//     * key: 指定要删除的数据
//     * allEntries = true  删除所有的数据
//     * beforeInvocation = false  缓存的清除是否是在方法之前执行，默认是在方法之后执行
//     * 如果在方法执行之后清空缓存，那么当方法执行过程中抛出异常，缓存就不会清除。
//     * <p>
//     * beforeInvocation = true  代表缓存清除操作是在方法之前执行，无论方法是否抛出异常，缓存都会清除。
//     */
//    @CacheEvict(cacheNames = {"emp"}/*, key = "#id", allEntries = true*/, beforeInvocation = true)
//    public void deleteEmpById(Integer id) {
//        System.out.println("deleteEmp: " + id);
////        employeeMapper.deleteEmpById(id);
//        int i = 10 / 0;
//    }
//
//
//    /**
//     * @Caching 定义复杂的缓存规则
//     * <p>
//     * Cacheable  缓存中有数据就从缓存中获取，否则就执行方法查询数据库，将返回值缓存
//     * CachePut   无论缓存中是否有数据，都会执行方法查询数据库，将返回值缓存
//     */
//    @Caching(
//            cacheable = {
//                    @Cacheable(/*cacheNames = "emp", */key = "#lastName")
//            },
//            put = {
//                    @CachePut(/*cacheNames = "emp", */key = "#result.id"),
//                    @CachePut(/*cacheNames = "emp", */key = "#result.email")
//            }
//    )
//    public Employee getEmpByLastName(String lastName) {
//        return employeeMapper.getEmpByLastName(lastName);
//    }
}
